﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using PowerBooking.RSE.Domain.Models;
using PowerBooking.RSE.Utility.Helpers;
using PowerBooking.RSE.Utility.Extends;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Collections.Concurrent;
using System.Data.Entity;
using PowerBooking.RSE.Poco;
using PowerBooking.RSE.Contract.Enums;
using PowerBooking.RSE.IRepository;
using System.Data.Entity.Validation;

namespace PowerBooking.RSE.Domain.Factories
{
    /// <summary>
    /// 餐厅工厂
    /// </summary>
    public class RestaurantFactory : Factory
    {
        public RestaurantFactory(IUnitOfWork unitOfWork) : base(unitOfWork) { }

        /// <summary>
        /// 创建餐厅集合
        /// </summary>
        /// <param name="cityUrl"></param>
        /// <param name="filterUrl"></param>
        /// <returns></returns>
        public List<Restaurant> CreateRestaurants(long spiderID, long cityID, long filterID, string cityUrl, string filterUrl)
        {
            var existRestaurantList = GetRestaurantsByCityID(cityID);
            var restaurantPocoList = new ConcurrentBag<RestaurantPoco>();
            var restaurants = new ConcurrentBag<Restaurant>();
            var html = WebRequestHelper.Get(filterUrl);

            var matches = Regex.Matches(html, RegexHelper.CreatePartternByAttributeName("class", "page_able"), RegexOptions.IgnoreCase).OfType<Match>().ToList();
            var pageCount = 1;
            if (matches.Count > 0)
            {
                pageCount = int.Parse(Regex.Match(matches[matches.Count - 2].Value, RegexHelper.CreateDom("a", RegexHelper.AnyAttributes, RegexHelper.AnyDomValueInGroup), RegexOptions.Singleline | RegexOptions.IgnoreCase).Groups[1].Value);
            }
            Parallel.For(1, pageCount, (p, loopStatePage) =>
            {

                if (SpiderMonitorStaticFactory.GetSpiderMonitor(spiderID).SpiderStatus != SpiderStatusEnum.Stop)
                {
                    var pageUrl = (filterUrl.LastIndexOf("/") + 1 == filterUrl.Length ? filterUrl.Substring(0, filterUrl.Length - 1) : filterUrl) + string.Format("-p{0}/", p.ToString());
                    var pageHtml = WebRequestHelper.Get(pageUrl);
                    var pageMatches = Regex.Matches(pageHtml, RegexHelper.CreatePartternByAttributeName("class", "(res_sch_res schResList|res_sch_detail)"), RegexOptions.Singleline | RegexOptions.IgnoreCase).OfType<Match>();
                    Parallel.ForEach(pageMatches, (m, loopStateItem) =>
                    {
                        if (SpiderMonitorStaticFactory.GetSpiderMonitor(spiderID).SpiderStatus != SpiderStatusEnum.Stop)
                        {
                            bool isNewVersion = Regex.Match(m.Value, RegexHelper.CreatePartternByAttributeName("class", "f14 di mr5"), RegexOptions.Singleline | RegexOptions.IgnoreCase).Success;
                            try
                            {
                                if (isNewVersion)
                                {
                                    restaurantPocoList.Add(CreateModernRestaurant(cityID, filterID, cityUrl, m.Value));
                                }
                                else
                                {
                                    restaurantPocoList.Add(CreateNormalRestaurant(cityID, filterID, cityUrl, m.Value));
                                }
                            }
                            catch
                            {

                            }
                        }
                        else
                        {
                            loopStateItem.Stop();
                            return;
                        }
                    });
                }
                else
                {
                    loopStatePage.Stop();
                    return;
                }

            });

            var restaurantPocos = restaurantPocoList.Distinct(r => r.Url, StringComparer.CurrentCultureIgnoreCase).ToList();
            for (var i = 0; i < restaurantPocos.Count; i++)
            {
                if (!existRestaurantList.Exists(e => e.Url.Equals(restaurantPocos[i].Url, StringComparison.CurrentCultureIgnoreCase)))
                {
                    UnitOfWork.RestaurantRepository.InsertRestaurant(restaurantPocos[i]);
                }
            }

            Parallel.ForEach(restaurantPocos.Where(r => (RestaurantTypeEnum)r.Type == RestaurantTypeEnum.Modern), (r) => { restaurants.Add(new ModernRestaurant().InitDomain(r)); });
            Parallel.ForEach(restaurantPocos.Where(r => (RestaurantTypeEnum)r.Type == RestaurantTypeEnum.Normal), (r) => { restaurants.Add(new NormalRestaurant().InitDomain(r)); });
            existRestaurantList.AddRange(restaurants);
            return existRestaurantList;

        }

        public List<Restaurant> GetRestaurantsByCityID(long cityID)
        {
            var restaurants = new ConcurrentBag<Restaurant>();
            var restaurantPocoList = UnitOfWork.RestaurantRepository.QueryRestaurantsByCityID(cityID);
            Parallel.ForEach(restaurantPocoList.Where(r => (RestaurantTypeEnum)r.Type == RestaurantTypeEnum.Modern), (r) => { restaurants.Add(new ModernRestaurant().InitDomain(r)); });
            Parallel.ForEach(restaurantPocoList.Where(r => (RestaurantTypeEnum)r.Type == RestaurantTypeEnum.Normal), (r) => { restaurants.Add(new NormalRestaurant().InitDomain(r)); });
            return restaurants.ToList();
        }

        private RestaurantPoco CreateModernRestaurant(long cityID, long filterID, string cityUrl, string restaurantHtml)
        {
            var restaurantPoco = new RestaurantPoco();
            restaurantPoco.CityID = cityID;
            restaurantPoco.FilterID = filterID;
            restaurantPoco.Type = (long)RestaurantTypeEnum.Modern;

            var nameMatch = Regex.Match(restaurantHtml, RegexHelper.CreatePartternByAttributeName("class", "f14 di mr5"), RegexOptions.Singleline | RegexOptions.IgnoreCase);
            restaurantPoco.Name = Regex.Match(nameMatch.Value, RegexHelper.CreateDom("a", RegexHelper.AnyAttributes, RegexHelper.AnyDomValueInGroup), RegexOptions.Singleline | RegexOptions.IgnoreCase).Groups[1].Value;
            restaurantPoco.Url = cityUrl + Regex.Match(nameMatch.Value, RegexHelper.CreateAttribute("href", RegexHelper.AnyAttributeValueInGroup), RegexOptions.Singleline | RegexOptions.IgnoreCase).Groups[1].Value;

            #region 明细

            var detailMatch = Regex.Match(restaurantHtml, RegexHelper.CreatePartternByAttributeName("class", "l pct60"), RegexOptions.Singleline | RegexOptions.IgnoreCase);
            var detailMatches = Regex.Matches(detailMatch.Value, RegexHelper.CreatePartternByAttributeName("class", "mb2"), RegexOptions.Singleline | RegexOptions.IgnoreCase).OfType<Match>();

            #region 菜系
            var cuisineMatch = detailMatches.Single<Match>(d => d.Value.Contains("菜系"));
            var cuisineMatches = Regex.Matches(cuisineMatch.Value, RegexHelper.CreatePartternByAttributeName("class", "dib mr5"), RegexOptions.Singleline | RegexOptions.IgnoreCase).OfType<Match>();
            var cuisines = new ConcurrentBag<string>();
            cuisineMatches.AsParallel().ForAll(c => { cuisines.Add(Regex.Match(c.Value, RegexHelper.CreateDom("a", RegexHelper.AnyAttributes, RegexHelper.AnyDomValueInGroup), RegexOptions.Singleline | RegexOptions.IgnoreCase).Groups[1].Value); });
            restaurantPoco.CuisineTags = string.Join(",", cuisines.ToList());
            #endregion

            #region 地址

            var _addressMatch = detailMatches.Single<Match>(d => d.Value.Contains("地址"));
            var addressMatch = Regex.Match(_addressMatch.Value, RegexHelper.CreatePartternByAttributeName("class", "cell pl5"), RegexOptions.Singleline | RegexOptions.IgnoreCase);
            restaurantPoco.Address = Regex.Replace(addressMatch.Value, RegexHelper.AnyHtmlDom, string.Empty, RegexOptions.Singleline | RegexOptions.IgnoreCase).Replace("&nbsp;", " ");
            var regionMatches = Regex.Matches(addressMatch.Value, RegexHelper.CreatePartternByAttributeName("class", "dib"), RegexOptions.Singleline | RegexOptions.IgnoreCase).OfType<Match>();
            var regions = new ConcurrentBag<string>();
            regionMatches.AsParallel().ForAll(r => { regions.Add(Regex.Match(r.Value, RegexHelper.CreateDom("a", RegexHelper.AnyAttributes, RegexHelper.AnyDomValueInGroup), RegexOptions.Singleline | RegexOptions.IgnoreCase).Groups[1].Value); });
            restaurantPoco.RegionTags = string.Join(",", regions.ToList());
            #endregion

            #region 人均

            var _perCapitaMatch = detailMatches.Single<Match>(d => d.Value.Contains("人均"));
            var perCapitaMatch = Regex.Match(_perCapitaMatch.Value, RegexHelper.CreatePartternByAttributeName("class", "cell pl5 cr"), RegexOptions.Singleline | RegexOptions.IgnoreCase);
            var perCapitaRangeMatch = Regex.Match(Regex.Replace(perCapitaMatch.Value, RegexHelper.AnyHtmlDom, string.Empty, RegexOptions.Singleline | RegexOptions.IgnoreCase), @"(\d+)[\-]?([\d]+)?", RegexOptions.Singleline | RegexOptions.IgnoreCase);
            if (perCapitaRangeMatch.Success)
            {
                if (perCapitaRangeMatch.Groups.Count == 3)
                {
                    restaurantPoco.MinPerCapita = int.Parse(perCapitaRangeMatch.Groups[1].Value);
                    restaurantPoco.MaxPerCapita = int.Parse(perCapitaRangeMatch.Groups[2].Value);
                }
                if (perCapitaRangeMatch.Groups.Count == 2)
                {
                    restaurantPoco.MinPerCapita = int.Parse(perCapitaRangeMatch.Groups[1].Value);
                    restaurantPoco.MaxPerCapita = int.Parse(perCapitaRangeMatch.Groups[1].Value);
                }
            }
            else
            {
                restaurantPoco.MinPerCapita = 0;
                restaurantPoco.MaxPerCapita = 0;
            }

            #endregion

            #endregion

            #region 质量

            var defaultQuality = 0d;
            var qualityMatch = Regex.Match(restaurantHtml, RegexHelper.CreatePartternByAttributeName("class", "l pl10 pct15"), RegexOptions.Singleline | RegexOptions.IgnoreCase);
            var qualityItemMatches = Regex.Matches(qualityMatch.Value, RegexHelper.CreateDom("span", RegexHelper.AnyAttributes, RegexHelper.AnyDomValueInGroup), RegexOptions.Singleline | RegexOptions.IgnoreCase).OfType<Match>().ToList();
            double.TryParse(qualityItemMatches[0].Groups[1].Value, out defaultQuality);
            restaurantPoco.Taste = defaultQuality;
            defaultQuality = 0d;
            double.TryParse(qualityItemMatches[1].Groups[1].Value, out defaultQuality);
            restaurantPoco.Environment = defaultQuality;
            defaultQuality = 0d;
            double.TryParse(qualityItemMatches[2].Groups[1].Value, out defaultQuality);
            restaurantPoco.Service = defaultQuality;
            defaultQuality = 0d;

            #endregion

            #region 其他

            var otherMatch = Regex.Match(restaurantHtml, RegexHelper.CreatePartternByAttributeName("class", "cell"), RegexOptions.Singleline | RegexOptions.IgnoreCase);
            var otherItemMatches = Regex.Matches(otherMatch.Value, RegexHelper.CreatePartternByAttributeName("class", "mb2"), RegexOptions.Singleline | RegexOptions.IgnoreCase);

            var recentOrdersMatch = Regex.Match(Regex.Replace(otherItemMatches[0].Value, RegexHelper.AnyHtmlDom, string.Empty, RegexOptions.Singleline | RegexOptions.IgnoreCase), @"\d+", RegexOptions.Singleline | RegexOptions.IgnoreCase);
            restaurantPoco.RecentOrders = recentOrdersMatch.Success ? int.Parse(recentOrdersMatch.Value) : 0;

            var reviewMatch = Regex.Match(Regex.Replace(otherItemMatches[1].Value, RegexHelper.AnyHtmlDom, string.Empty, RegexOptions.Singleline | RegexOptions.IgnoreCase), @"\d+", RegexOptions.Singleline | RegexOptions.IgnoreCase);
            restaurantPoco.Reviews = reviewMatch.Success ? int.Parse(reviewMatch.Value) : 0;

            var peopleLikeRateMatch = Regex.Match(Regex.Replace(otherItemMatches[2].Value, RegexHelper.AnyHtmlDom, string.Empty, RegexOptions.Singleline | RegexOptions.IgnoreCase), @"\d+", RegexOptions.Singleline | RegexOptions.IgnoreCase);
            restaurantPoco.PeopleLikeRate = peopleLikeRateMatch.Success ? int.Parse(peopleLikeRateMatch.Value) : 0;


            #endregion

            return restaurantPoco;
        }

        private RestaurantPoco CreateNormalRestaurant(long cityID, long filterID, string cityUrl, string restaurantHtml)
        {
            var restaurantPoco = new RestaurantPoco();
            restaurantPoco.CityID = cityID;
            restaurantPoco.FilterID = filterID;
            restaurantPoco.Type = (long)RestaurantTypeEnum.Normal;

            var defaultDouble = 0d;

            var nameMatch = Regex.Match(restaurantHtml, RegexHelper.CreatePartternByAttributeName("class", "f14"), RegexOptions.Singleline | RegexOptions.IgnoreCase);
            restaurantPoco.Name = Regex.Replace(nameMatch.Value, RegexHelper.AnyHtmlDom, string.Empty, RegexOptions.Singleline | RegexOptions.IgnoreCase).Trim();
            restaurantPoco.Url = cityUrl + Regex.Match(nameMatch.Value, RegexHelper.CreateAttribute("href", RegexHelper.AnyAttributeValueInGroup), RegexOptions.Singleline | RegexOptions.IgnoreCase).Groups[1].Value;

            #region 菜系

            var cuisineMatch = Regex.Match(restaurantHtml, RegexHelper.CreatePartternByAttributeName("class", "pt5"), RegexOptions.Singleline | RegexOptions.IgnoreCase);
            var cuisineMatches = Regex.Matches(cuisineMatch.Value, RegexHelper.CreateDom("a", RegexHelper.AnyAttributes, RegexHelper.AnyDomValueInGroup), RegexOptions.Singleline | RegexOptions.IgnoreCase).OfType<Match>();
            var cuisines = new ConcurrentBag<string>();
            cuisineMatches.AsParallel().ForAll(c => { cuisines.Add(c.Groups[1].Value); });
            restaurantPoco.CuisineTags = string.Join(",", cuisines.ToList());
            #endregion

            #region 地址

            var addressMatch = Regex.Match(restaurantHtml, RegexHelper.CreatePartternByAttributeName("class", "pb2 pt5"), RegexOptions.Singleline | RegexOptions.IgnoreCase);
            restaurantPoco.Address = Regex.Replace(Regex.Match(addressMatch.Value, RegexHelper.CreatePartternByAttributeName("class", "cell"), RegexOptions.Singleline | RegexOptions.IgnoreCase).Value, RegexHelper.AnyHtmlDom, string.Empty, RegexOptions.Singleline | RegexOptions.IgnoreCase).Replace("&nbsp;", string.Empty);
            var regionMatches = Regex.Matches(addressMatch.Value, RegexHelper.CreateDom("a", RegexHelper.AnyAttributes, RegexHelper.AnyDomValueInGroup), RegexOptions.Singleline | RegexOptions.IgnoreCase).OfType<Match>().ToList();
            var regions = new ConcurrentBag<string>();
            regionMatches.ForEach(r => { regions.Add(r.Groups[1].Value); });
            restaurantPoco.RegionTags = string.Join(",", regions.ToList());
            #endregion

            #region 人均

            var perCapitaMatch = Regex.Match(restaurantHtml, RegexHelper.CreatePartternByAttributeName("class", "pb2 pt2 fix"), RegexOptions.Singleline | RegexOptions.IgnoreCase);
            var perCapitaRangeMatch = Regex.Match(Regex.Replace(perCapitaMatch.Value, RegexHelper.AnyHtmlDom, string.Empty, RegexOptions.Singleline | RegexOptions.IgnoreCase), @"(\d+)[\-]?([\d]+)?", RegexOptions.Singleline | RegexOptions.IgnoreCase);
            if (perCapitaRangeMatch.Success)
            {
                if (perCapitaRangeMatch.Groups.Count == 3)
                {
                    restaurantPoco.MinPerCapita = int.Parse(perCapitaRangeMatch.Groups[1].Value);
                    restaurantPoco.MaxPerCapita = int.Parse(perCapitaRangeMatch.Groups[2].Value);
                }
                if (perCapitaRangeMatch.Groups.Count == 2)
                {
                    restaurantPoco.MinPerCapita = int.Parse(perCapitaRangeMatch.Groups[1].Value);
                    restaurantPoco.MaxPerCapita = int.Parse(perCapitaRangeMatch.Groups[1].Value);
                }
            }
            else
            {
                restaurantPoco.MinPerCapita = 0;
                restaurantPoco.MaxPerCapita = 0;
            }

            #endregion

            #region 质量

            var qualityMatch = Regex.Match(restaurantHtml, RegexHelper.CreatePartternByAttributeName("class", "btd pt5 mt10"), RegexOptions.Singleline | RegexOptions.IgnoreCase);
            var qualityItemMatches = Regex.Matches(qualityMatch.Value, RegexHelper.CreateDom("strong", RegexHelper.AnyAttributes, RegexHelper.AnyDomValueInGroup), RegexOptions.Singleline | RegexOptions.IgnoreCase).OfType<Match>().ToList();
            double.TryParse(qualityItemMatches[0].Groups[1].Value, out defaultDouble);
            restaurantPoco.Taste = defaultDouble;
            defaultDouble = 0d;
            double.TryParse(qualityItemMatches[1].Groups[1].Value, out defaultDouble);
            restaurantPoco.Environment = defaultDouble;
            defaultDouble = 0d;
            double.TryParse(qualityItemMatches[2].Groups[1].Value, out defaultDouble);
            restaurantPoco.Service = defaultDouble;
            defaultDouble = 0d;

            #endregion

            #region 其他


            double.TryParse(Regex.Replace(Regex.Match(restaurantHtml, RegexHelper.CreatePartternByAttributeName("class", "res_o_list co ml10"), RegexOptions.Singleline | RegexOptions.IgnoreCase).Value, RegexHelper.AnyHtmlDom, string.Empty, RegexOptions.Singleline | RegexOptions.IgnoreCase).Trim(), out defaultDouble);
            restaurantPoco.Impress = defaultDouble;
            defaultDouble = 0d;
            var reviewsMatch = Regex.Match(restaurantHtml, @"(\d+)封点评", RegexOptions.Singleline | RegexOptions.IgnoreCase);
            restaurantPoco.Reviews = reviewsMatch.Success ? int.Parse(reviewsMatch.Groups[1].Value) : 0;
            var recentOrdersMatch = Regex.Match(restaurantHtml, @"(\d+)笔近期订单", RegexOptions.Singleline | RegexOptions.IgnoreCase);
            restaurantPoco.RecentOrders = recentOrdersMatch.Success ? int.Parse(recentOrdersMatch.Groups[1].Value) : 0;

            #endregion

            return restaurantPoco;
        }
    }
}
